--[[
导出到c++接口
每个类型都会实现 cpp_cast来转换

]]
local format = string.format;
function cppType(def)
    local list = GetCppType(nil,"XQt");
    local types = {};
    local typedeffile = io.tmpfile();
    local baseList = {};

    --新的导出方案
    local lcxxm_tfile = io.tmpfile();
    local lcxxm_codefile = io.tmpfile();

    for k,v in ipairs(list)do
        --v = cast('CXXType',nil);
        local m = v:GetMethods();
        local className = v:GetTypeName();
        local classn = className;
        local baselist = v:GetBase();
        local lcname,cxxname = v:GetLCName();
        local typedef = format('typedef %s %s;\n',cxxname,lcname);
        --有关类型别名的处理
        local aliasBase,aliasList = v:GetUnqlType();
        local aliasBaseName = aliasBase:GetLCName();
        for k,v in ipairs(aliasList)do
            local aliasName = v:GetLCName();
            lcxxm_tfile:write(format('def_alias(%s,%s);\n',aliasBaseName,aliasName));
        end

        lcxxm_tfile:write(format('lregtypemethod(%s);\n',lcname));
        typedeffile:write(typedef);
        if(m and #m)then
            local funcs = {};
            --把className 设置为lcname
            className = lcname; 
            types[className] = funcs;  --获得所有方法
            baseList[className] = baselist;
            for kk,vv in ipairs(m)do
                local cname,fname,tname = vv:GetLCName();
                local param_begin = 2;
                local nodump = false;
                if(vv.isctor)then
                    param_begin = 1;
                    if(classn~=fname)then
                        nodump = true;
                    else
                        cname = '.new';
                        fname = '.new';
                        table.remove(vv.args,1);
                    end
                elseif(vv.isdtor)then
                    nodump = true;
                end
                if(nodump~=true)then
                    --构造函数
                    if(funcs[fname]==nil)then funcs[fname] = {} end;
                    --返回值设定
                    local ret = '';
                    local saveret = '';
                    local pushretcode = '';
                    local retcode = 'return 0;';
                    local rtn = vv.ret;
                    local rtcname,rtcxxname = rtn:GetLCName();
                    if(rtcxxname=='void')then
                        --无返回值
                    else
                        
                        if(rtcxxname=='int')then
                            saveret = '  int ret = ';
                            pushretcode = '  return lpushint(L,ret);';
                        elseif(rtcxxname=='float')then
                            saveret = '  float ret = ';
                            pushretcode = '  return lpushnum(L,ret);';
                        elseif(rtcxxname=='bool')then
                            saveret = '  auto ret = ';
                            pushretcode = 'return lpush_bool(L,ret);';
                        else
                            saveret = '  auto ret = ';
                            pushretcode = '  return lpush_obj(L,ret,"RetTyp");';
                        end
                    end
                    pushretcode = pushretcode:gsub('RetTyp',rtcname);

                    local list = funcs[fname];
                    local text = 'if((argtop==%d)';
                    text = format(text,#vv.args-1);
                    local _and = '';
                    local arg = '';
                    local cma = '';
                    local decl = '';
                    local lcxxm_args = '';
                    for param=param_begin,#vv.args do
                        local tname,ptr,ref,lcname = vv:GetLCParmType(param);
                        if(tname=='void' and ptr==nil and ref==nil)then
                            lcxxm_args = lcxxm_args .. ' 0,\n';
                            break;
                        elseif(tname)then
                            if(ref==nil and ptr==nil and tname=='void')then
                                break;
                            end
                            _and = '&&';
                            local basetype = {
                                ['int'] = 'int',
                                ['float'] = 'float',
                                ['bool'] = 'bool',
                                ['char_ptr'] = 'char_ptr',
                            };

                            local call = '';
                            local code = '(lgetcxx%s(L,%d,"%s",&v%d))';

                            if(ref)then
                                lcxxm_args = lcxxm_args .. format('"%s",\n',tname);
                                arg = arg .. cma .. '*v'..param;
                                decl = decl .. format('%s* v%d;\n',tname,param);
                            elseif(ptr==1)then
                                arg = arg .. cma .. 'v' ..param;
                                decl = decl .. format('%s* v%d;\n',tname,param);
                                lcxxm_args = lcxxm_args .. format('"%s",\n',tname);
                            elseif(ptr==nil)then
                                if(basetype[tname])then
                                    call = basetype[tname];
                                end
                                --直接是结构体
                                arg = arg .. cma .. 'v'..param;
                                decl = decl .. format('%s v%d;\n',tname,param);
                                lcxxm_args = lcxxm_args .. format('"%s",\n',tname);
                            end
                            code = format(code,call,param,lcname,param);
                            text = text .. _and .. code;
                            cma = ','
                        else
                            --遇到不能处理的类型
                            text = '';
                            break;
                        end
                    end
                    if(text~='')then
                        text = text .. '){\n'
                        if(vv.isctor)then
                            text = text .. format(' %s(%s);\n',cname,arg);
                        else
                            text = text .. format('%s _this->%s(%s);\n%s\n',saveret,fname,arg,pushretcode);
                        end
                        local lcxxm_new = '{\n ' .. decl .. text;
                        text = text .. '}\n';
                        text = '{\n ' .. decl .. text .. '};\n';

                        table.insert( list,text );

                        local _this = '';
                        if(fname=='.new')then
                            local a = '\nreturn init(L,_this,"$$Type",$$Typedel,$$Typefunc,$$Typecast);'
                            fname = 'new';
                            text = lcxxm_new .. a .. '\n};\n};\n'
                            text = text:gsub('%.new','_this = new '..className);
                            text = text:gsub('$$Type',className);
                        else
                            _this = 'if(lgetcxx(L,1,"$$Type",&_this))'
                        end
                        lcxxm_codefile:write(format('const char* %s%d%sArgs%d[] = { %s };\n',className,#fname,fname,#list,lcxxm_args));
                        lcxxm_codefile:write(format('static int lcxxm_%s%d%s%d(lua_State*L){\n',className,#fname,fname,#list));
                        local code = [[
    $$Type *_this = 0;// = new Type();
    int argtop = lua_gettop(L);
    %s
]]
                        code = format(code,_this);
                        code = code:gsub('$$Type',className);
                        lcxxm_codefile:write(code);
                        lcxxm_codefile:write(text);
                        lcxxm_codefile:write('return 0;\n }\n')
                        lcxxm_tfile:write(format('    lregmethod(%s,%s,%d%s,%d);\n',lcname,fname,#fname,fname,#list));
                    end
                end
            end
        end
    end

    local function readf(f)
        f:seek('set');
        return f:read('*a');
    end

    --local lregfunc = 'luaL_Reg Typefunc[]={\n';
    local out = io.stdout;
    local casttmp = '\n    if(!strcmp("$$Base",t)){ (($cxxBase **)ptr)[0] = _this;return 1;}';
    print(readf(typedeffile));
    
    --注册函数
    local libsfile = io.tmpfile();
    libsfile:write('static int regtypes(lua_State*L){\n');

    --遍历所有类
    for k,v in pairs(types)do
        --前缀名
        local typeName = k;
        libsfile:write(format(' lregtype(L,%s);\n',typeName));

        local regfuncfile = io.tmpfile();
        local codefile = io.tmpfile();
        local castcode = '';
        
        local lcxxm_getobjs = '\n';

        --处理继承关系
        if(baseList[k])then
            local function proc(list)
                if(list==nil)then return; end;
                for kk,v in ipairs(list)do
                    local baseName,cxxname = v:GetLCName();
                    castcode = castcode .. casttmp:gsub('$cxxBase',cxxname);
                    castcode = castcode:gsub('$$Base',baseName);
                    local base = v:GetBase();
                    proc(base);

                    --新处理模板
                    lcxxm_getobjs = lcxxm_getobjs .. format('push_typecastobj(%s,%s,%s)\n',k,cxxname,baseName);

                end
            end
            proc(baseList[k]);
        end

        --处理类型创建
        local createCode = [[

static int $$Typecast(LCXX_Data*data,const char*t,void*ptr){
    if(ptr){
        $$Type* _this = ($$Type*)data->_this;
        //if(!strcmp("Base",t)){ ((Base **)ptr)[0] = ($$Type*)_this;return 1} %s
    }
    return 0;
}           

static void $$Typedel(void*self){
    $$Type *_this = ($$Type*)self;
    delete _this;
}

static int lcxx_$$Typenew(lua_State*L){
    $$Type *_this = 0;// = new Type();
    int argtop = lua_gettop(L);
    //创建新的对象%s
    return init(L,_this,"$$Type",$$Typedel,$$Typefunc,$$Typecast);
}

static int lcxx_$$Typecast(lua_State*L){
    if(lua_islightuserdata(L,1)){
        int gc = 1; //默认是不清理
        void* p = lua_touserdata(L,1);
        if(lua_isinteger(L,2)){
            gc = lua_tointeger(L,2);
        }
        return init(L,p,"$$Type",$$Typedel,$$Typefunc,$$Typecast,gc);
    }
    return 0;
}

static int lcxxm_objs$$Type(lua_State*L){
    $$Type *_this;
    int ret = 0;
    if(lgetcxx(L,1,"$$Type",&_this)){
        void* p;
        //push_typecastobj(_3XQt10MainWindow,QMainWindow,_11QMainWindow);%s
    }
    return ret;
}
]];
        local new = "\n";  --创建new方法
        
        
        --遍历方法
        for funcName,l in pairs(v)do
            --local fname = format('%d%s',#funcName,funcName);
            if(funcName=='.new')then
                for k,v in ipairs(l)do
                    new = new .. v;
                end
            else
                local code = [[
static int lcxx$$TypeField(lua_State*L){
    $$Type *_this;
    int argtop = lua_gettop(L);
    lassert(lgetcxx(L,1,"$$Type",&_this));
    //%s

    return 0;
}

]];
                local cc = '';
                for k,v in ipairs(l)do
                    cc = cc .. v;
                end
                code = code:gsub('Field',tostring(#funcName)..funcName);
                --跳转分支
                code = format(code,'\n'..cc);
                codefile:write(code);

                --注册函数
                regfuncfile:write(format('{"%s",lcxx%s},\n',funcName,typeName..tostring(#funcName)..funcName));

                
            end
            --print(k,funcName);
        end

        --创建lcxx_%Type%new,lcxx_%Type%del 函数
        createCode = format(createCode,castcode,new,lcxxm_getobjs);
        createCode = createCode:gsub('$$Type',k);
        createCode = createCode:gsub('%.new','_this = new '..typeName);

        --
        local reg = readf(regfuncfile);
        reg = 'luaL_Reg $$Typefunc[]={\n' .. reg .. '{0,0},};\n'
        reg = reg:gsub('$$Type',k);
        local code = readf(codefile);
        code = code:gsub('$$Type',typeName);
        --print(code);
        --print(reg);

        --新代码模板
        reg = '#define $$Typefunc ((luaL_Reg*)0)'
        reg = reg:gsub('$$Type',k);
        print(reg);
        print(createCode);
    end
    libsfile:write('return 0;\n}\n');
    --print(readf(libsfile));

    print(readf(lcxxm_codefile));
    print('static int test(lua_State*L){\n')
    print(readf(lcxxm_tfile));
    print('}\n');

    return list;
end




local luaCppDef = {
    exportType = cppType,
}

return luaCppDef;

